#include "Precomp.h"
#include "IEntity.h"
#include "PluggableFactory.h"
#include "MapState.h"
#include "mapformat.pb.h"
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/construct.hpp>

using namespace boost;

namespace SGF
{
	IEntity::IEntity()
		:map(0),active(false),node(0),parent(0)
	{
	}

	IEntity::~IEntity()
	{
		std::for_each(childEntityList.begin(),childEntityList.end(),
			lambda::bind(lambda::delete_ptr(),lambda::_1));//delete all children
	}

	IEntity* IEntity::clone() const
	{
		mapformat::Entity data;
		SaveData sdata(&data);
		onSave(sdata);
		PluggableFactory& factory=PluggableFactory::getInstance();
		IEntity* newEnt=map->createEntity(factory.getProductName(this));
		newEnt->onLoad(sdata);
		return newEnt;
	}

	void IEntity::onStateBegin(){}
	void IEntity::onStateEnd(){}
	void IEntity::onUpdate(Ogre::Real timeSinceLastFrame){}
	void IEntity::onChildAttached(IEntity* child){}
	void IEntity::onChildDetached(IEntity* child){}

	void IEntity::attachTo(IEntity* _parent)
	{
		if(parent)
		{
			parent->childEntityList.remove(this);//free this entity from the old parent
			parent->onChildDetached(this);//inform the parent
		}
		else
			map->removeEntity(this);//free this entity from the map

		parent=_parent;
		if(parent)
		{
			parent->childEntityList.push_back(this);//add this entity to the new parent
			parent->onChildAttached(this);//inform the parent
		}
		else
			map->addEntity(this);//add this entity to the map
	}

	IEntity* IEntity::getParent()
	{
		return parent;
	}

	void IEntity::onLoad(const SaveData& saveData)
	{
		const mapformat::Entity& dat=*(saveData.innerData);
		mapformat::Vector vector=dat.position();
		setPosition(Ogre::Vector3(vector.x(),vector.y(),vector.z()));
		vector=dat.rotation();
		setRotation(Ogre::Vector3(vector.x(),vector.y(),vector.z()));
		vector=dat.scale();
		setScale(Ogre::Vector3(vector.x(),vector.y(),vector.z()));
	}

	void IEntity::setNode(Ogre::SceneNode* newNode)
	{
		node->setUserAny(Any((IEntity*)0));//reset user data
		node=newNode;
		Any any(this);
		node->setUserAny(any);
	}

	void IEntity::onSave(SaveData& saveData) const
	{
		mapformat::Entity& dat=*(saveData.innerData);
		mapformat::Vector* vec;
		Ogre::Vector3 vector=getPosition();
		vec=dat.mutable_position();
		vec->set_x(vector.x);
		vec->set_y(vector.y);
		vec->set_z(vector.z);
		vector=getRotation();
		vec=dat.mutable_rotation();
		vec->set_x(vector.x);
		vec->set_y(vector.y);
		vec->set_z(vector.z);
		vector=getScale();
		vec=dat.mutable_scale();
		vec->set_x(vector.x);
		vec->set_y(vector.y);
		vec->set_z(vector.z);
	}

	const Ogre::Vector3 IEntity::getRotation() const
	{
		const Quaternion& quart=node->getOrientation();
		return Vector3(quart.getPitch().valueDegrees(),quart.getRoll().valueDegrees(),quart.getYaw().valueDegrees());
	}
	#define DEG2RAD(X) (X)/180.f*3.14159265f
	void IEntity::setRotation(const Ogre::Vector3& rot)
	{
		node->pitch(Radian(DEG2RAD(rot.x)),Node::TS_PARENT);
		node->roll(Radian(DEG2RAD(rot.y)),Node::TS_PARENT);
		node->yaw(Radian(DEG2RAD(rot.z)),Node::TS_PARENT);
	}

	const Ogre::Vector3& IEntity::getPosition() const
	{
		return node->getPosition();
	}

	void IEntity::setPosition(const Ogre::Vector3& pos)
	{
		node->setPosition(pos);
	}

	const Ogre::Vector3&  IEntity::getScale() const
	{
		return node->getScale();
	}

	void IEntity::setScale(const Ogre::Vector3& scale)
	{
		node->setScale(scale);
	}
}